(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global.godebug = factory());
}(this, (function () { 'use strict';

var ajax = function ajax(opt) {
    var xhr = new XMLHttpRequest();
    // new ActiveXObject('')


    xhr.open(opt.type || 'post', opt.url, true);
    xhr.setRequestHeader("Content-Type", 'application/json');
    xhr.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            opt.success && opt.success();
        }
    };
    //_log('opt.data')
    /// _log(opt.data)
    //_log(JSON.stringify(opt.data))

    xhr.send(JSON.stringify(opt.data));
};

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  return typeof obj;
} : function (obj) {
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};

var utils = {
    getReqUrl: function getReqUrl(url) {
        var a = document.createElement('a');
        a.href = url;
        return a.href;
    },
    isFunction: function isFunction() {
        return typeof obj === "function";
    },
    isWindow: function isWindow() {
        return obj != null && obj == obj.window;
    },
    isArray: function isArray() {
        return obj instanceof Array;
    },
    isPlainObject: function isPlainObject() {
        if (!obj || this.type(obj) !== "object" || obj.nodeType || this.isWindow(obj)) {
            return false;
        }

        try {
            // Not own constructor property must be Object
            if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
                return false;
            }
        } catch (e) {
            // IE8,9 Will throw exceptions on certain host objects #9897
            return false;
        }

        // Own properties are enumerated firstly, so to speed up,
        // if last one is own, then all properties are own.

        var key;
        for (key in obj) {}

        return key === undefined || core_hasOwn.call(obj, key);
    },
    getTime: function getTime() {
        return +new Date();
    },
    extend: function extend() {
        var src,
            copyIsArray,
            copy,
            name,
            options,
            clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;

        // Handle a deep copy situation
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[1] || {};
            // skip the boolean and the target
            i = 2;
        }

        // Handle case when target is a string or something (possible in deep copy)
        if ((typeof target === "undefined" ? "undefined" : _typeof(target)) !== "object" && !this.isFunction(target)) {
            target = {};
        }

        // extend jQuery itself if only one argument is passed
        if (length === i) {
            target = this;
            --i;
        }

        for (; i < length; i++) {
            // Only deal with non-null/undefined values
            if ((options = arguments[i]) != null) {
                // Extend the base object
                for (name in options) {
                    src = target[name];
                    copy = options[name];

                    // Prevent never-ending loop
                    if (target === copy) {
                        continue;
                    }

                    // Recurse if we're merging plain objects or arrays
                    if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = this.isArray(copy)))) {
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && this.isArray(src) ? src : [];
                        } else {
                            clone = src && this.isPlainObject(src) ? src : {};
                        }

                        // Never move original objects, clone them
                        target[name] = this.extend(deep, clone, copy);

                        // Don't bring in undefined values
                    } else if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }

        // Return the modified object
        return target;
    }
};

// var version = require('../package.json').version

// console.log('ajax',ajax)
var Godebug = function Godebug() {
    //this.prototype. //存储事件的对象 
    //软件版本
    this.version = 'version';
    //上报key
    this.appKey = '';
    //基本上报路径
    this.url = '';
    //性能上报url
    this.performanceURL = '';
    // 错误上报url
    this.errorURL = '//127.0.0.1:3002/error/report';
    //错误上报时间 true 立即 false不上报 其他数字为具体单位时间 秒
    this.errorReportTime = true;
    // 性能上报时间 'load'
    /*
     load 资源加载完成
     ready dom解析完成
     resourcesLoad ready前请求的接口 加载完成
    */
    this.performanceReportTime = true;
};
Godebug.prototype = {
    $handlers: {},
    // 上报记录的事件类型
    reportEvent: ['click'],
    //上报简单模式，采用img上报(没有详细记录，只有错误信息) 否则采用 post
    simple: false,

    //上报日志级别
    levels: ['log'],
    // 最大记录条数
    maxRecordCount: 20,
    // 资源不监控
    silentResource: false,
    // http请求不监控
    silentHttp: false,
    //控制台日志不监控
    silentConsole: false,
    constructor: Godebug,
    $$reqUrl: {},
    $$reqUrlCount: 0,
    isReady: false,
    // 监听事件
    on: function on(type, handler) {
        if (typeof this.$handlers[type] == 'undefined') this.$handlers[type] = [];
        this.$handlers[type].push(handler); //将要触发的函数压入事件函数命名的数组中
    },
    off: function off(type, $handlers) {
        if (!this.$handlers[type]) return;
        var $handlers = this.$handlers[type];
        if (handler == undefined) {
            $handlers.length = 0; //不传某个具体函数时，解绑所有
        } else if ($handlers.length) {
            for (var i = 0; i < $handlers.length; i++) {
                if ($handlers[i] == handler) {
                    //解绑单个
                    this.$handlers[type].splice(i, 1);
                }
            }
        }
    },
    emit: function emit(type, data) {
        if (this.$handlers[type] instanceof Array) {
            var $handlers = this.$handlers[type];
            //遍历事件函数监听的程序，依次执行
            for (var i = 0; i < $handlers.length; i++) {
                //    $handlers[i](data);
                //  _log(data)
                $handlers[i].call(this, data);
            }
        }
    },
    log: function log(msg, send) {
        // debugger
        if (!this.$msg_id) {
            this.$msg_id = utils.getTime();
            this.$msg = [];
        }
        if (typeof msg != 'string') {
            msg = JSON.stringify(msg);
        }
        this.$msg.push({
            time: utils.getTime(),
            msg: msg
        });
        send && this.logEnd();
    },
    logEnd: function logEnd() {
        _log('消息上报');
        _log(this.$msg);
        var data = {
            msg: this.$msg,
            type: 'wx'
        };
        this.emit('sendLog', data);
    },
    notifyError: function notifyError() {},
    notifyHttpError: function notifyHttpError() {},
    notify: function notify(msg) {
        this.emit('report', {
            mes: msg
        });
        console.log('请查看右键');
    }
};

// Godebug
var godebug = new Godebug();

var performance = (function () {
    var performance = window.performance;
    if (!performance) {
        return false;
    }
    var entries = performance.getEntries();
    var data = {};
    for (var i = 0; i < entries.length; i++) {

        data[entries[i].name] = {
            url: entries[i].name,
            entryType: entries[i].entryType,
            //startTime:entries[i].requestStart,
            duration: +entries[i].duration.toFixed(2)
        };
    }
    return data;
});

var timing = (function () {
   var performance = window.performance;

   if (!performance) {
      // 当前浏览器不支持
      console.log('你的浏览器不支持 performance 接口');
      return;
   }

   var t = performance.timing;
   var times = {};

   //【重要】页面加载完成的时间
   //【原因】这几乎代表了用户等待页面可用的时间
   times.loadPage = t.loadEventEnd - t.navigationStart;

   //【重要】解析 DOM 树结构的时间
   //【原因】反省下你的 DOM 树嵌套是不是太多了！
   times.domReady = t.domComplete - t.responseEnd;

   //【重要】重定向的时间
   //【原因】拒绝重定向！比如，http://example.com/ 就不该写成 http://example.com
   times.redirect = t.redirectEnd - t.redirectStart;

   //【重要】DNS 查询时间
   //【原因】DNS 预加载做了么？页面内是不是使用了太多不同的域名导致域名查询的时间太长？
   // 可使用 HTML5 Prefetch 预查询 DNS ，见：[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)            
   times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;

   //【重要】读取页面第一个字节的时间
   //【原因】这可以理解为用户拿到你的资源占用的时间，加异地机房了么，加CDN 处理了么？加带宽了么？加 CPU 运算速度了么？
   // TTFB 即 Time To First Byte 的意思
   // 维基百科：https://en.wikipedia.org/wiki/Time_To_First_Byte
   times.ttfb = t.responseStart - t.navigationStart;

   //【重要】内容加载完成的时间
   //【原因】页面内容经过 gzip 压缩了么，静态资源 css/js 等压缩了么？
   times.request = t.responseEnd - t.requestStart;

   //【重要】执行 onload 回调函数的时间
   //【原因】是否太多不必要的操作都放到 onload 回调函数里执行了，考虑过延迟加载、按需加载的策略么？
   times.loadEvent = t.loadEventEnd - t.loadEventStart;

   // DNS 缓存时间
   times.appcache = t.domainLookupStart - t.fetchStart;

   // 卸载页面的时间
   times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;

   // TCP 建立连接完成握手的时间
   times.connect = t.connectEnd - t.connectStart;

   return times;
});

var baseData = (function () {
    return {
        url: location.href,
        title: document.title,
        key: godebug.key,
        ua: navigator.userAgent,
        platform: navigator.platform,
        screen: window.screen.height + '*' + window.screen.width,
        time: +new Date()

    };
});

(function () {
    /*
    open 请求打开事件
    send 请求发送事件
    ready 当 DOM（文档对象模型） 已经加载完成
    error 页面js错误事件
    report jss上报事件
    httpDone    http请求完成
    notFound    资源没找到
    httpError  服务器错误
    httpComplete
    data{
        type 错误事件类型
        message 错误信息
        lineno  行号
        colno 列号
        filename 文件名
        stack 堆栈信息
        timeStamp 错误时间
    }
    */
    godebug.on('load', function (res) {
        _log('load', '源加载完毕');
        godebug.emit('performanceReport');
    });
    godebug.on('ready', function (_) {
        _log('ready');

        _log(godebug.$$reqUrl);
        // return false
        godebug.emit('performanceReport');
    });
    godebug.on('httpComplete', function (res) {
        //   _log('httpComplete', res)
        if (this.$$reqUrl[utils.getReqUrl(res.reqUrl)]) {
            this.$$reqUrl[utils.getReqUrl(res.reqUrl)] = 'completed';
            this.$$reqUrlCount--;
            _log('load', this.$$reqUrlCount);
            if (this.$$reqUrlCount == 0) {
                this.emit('load');
            }
        }
    });
    godebug.on('notFound', function (res) {
        _log('notFound', res);
        // godebug.$$reqUrl[res.reqUrl] = 'completed';
    });
    godebug.on('performanceReport', function () {
        var data = {
            timing: timing(),
            performance: performance()
            // debugger;
        };data.reportURL = 'http://127.0.0.1:3002/performance/report';
        godebug.emit('report', data);
    });
    godebug.on('report', function (data) {
        ///  _log('上报')
        utils.extend(data, baseData());
        // data.key = 'ssss'
        _log(baseData);
        if (godebug.simple) {
            var d = [];
            for (var item in data) {
                var value = data[item];
                if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'object') {
                    value = JSON.stringify(value);
                }
                d.push(item + '=' + value);
            }
            new Image().src = data.reportURL + '?' + d.join('&');
        } else {
            ajax({
                url: data.reportURL + '?key=' + data.key,
                // header:{
                //   key:data.key,
                // },
                data: data
            });
        }
    });
    godebug.on('resourceError', function (data) {
        _log('resourceError', data);
        //data.type =
        godebug.emit('error', data);
    });
    godebug.on('error', function (data) {
        // data.baseData = baseData;
        //utils.extend(data,baseData)
        _log('发生错误', data);

        //debuggerss
        data.reportURL = godebug.errorURL;
        //状态
        data.status = 0;
        // debuggersss
        return;
        godebug.emit('report', data);
        _log('error');
        try {
            _log(JSON.stringify(data));
        } catch (error) {
            _log(data);
        }
    });
    godebug.on('send', function (res) {
        // console.log('send', utils.getReqUrl(res.reqUrl))
        _log('send', this.isReady, res.reqUrl);
        if (!this.isReady) {
            _log('*****************', utils.getReqUrl(res.reqUrl));
            this.$$reqUrlCount++;
            this.$$reqUrl[utils.getReqUrl(res.reqUrl)] = 'pending';
        }
    });
    godebug.on('sendLog', function (res) {
        ajax({
            url: '//127.0.0.1:3002/sendLog',
            // type:'get',
            data: res
        });
    });
})();

(function () {

    var levels = godebug.levels;
    window._levels = {};

    var _loop = function _loop(i) {
        window._levels[levels[i]] = console[levels[i]];
        // let level = console[levels[i]];
        window.console[levels[i]] = function () {
            var data = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                data.push(arguments[_i]);
            }
            godebug.emit('log', data);
            try {
                //ie8及以下如果设置了安全级别比较高的，console.apply会抛异常
                window._levels[levels[i]].apply(console, arguments);
            } catch (error) {
                var a = Array.prototype.slice.apply(arguments).join(" ");
                window._levels[levels[i]](a);
            }
        };
    };

    for (var i = 0; i < levels.length; i++) {
        _loop(i);
    }
    try {
        //简写了log输出，这个不会调重写后的console bind为了输出原来打印的地方
        window._log = window._levels['log'].bind(console);
    } catch (error) {
        window._log = function () {
            try {
                //安全级别
                window._levels['log'].apply(console, arguments);
            } catch (error) {
                var a = Array.prototype.slice.apply(arguments).join(" ");
                window._levels['log'](a);
            }
        };
    }
})();

(function () {
    // _log('default XMLHttpRequest')
    var XHR = XMLHttpRequest.prototype;
    if (!XHR) return;
    var open = XHR.open;
    // var onreadystatechange = XHR.onreadystatechange;

    var type = void 0;
    XHR.open = function (method, reqUrl) {
        type = method;
        this.reqUrl = reqUrl;
        //  _log(this)
        godebug.emit('open', [type, reqUrl]);
        try {
            open.apply(this, arguments);
        } catch (error) {
            _log('open****************');
            _log(error);
        }
    };
    var send = XHR.send;
    XHR.send = function (a, b) {
        //  _log(this)
        var _this = this,
            onreadystatechange = _this.onreadystatechange;
        this.addEventListener && this.addEventListener('readystatechange', function () {
            if (_this.readyState == 4) {
                var data = {
                    reqUrl: this.reqUrl
                };
                godebug.emit('httpComplete', data);

                //_log(this)
                switch (_this.status) {
                    case 200:
                        godebug.emit('httpDone', data);
                        break;
                    case 404:
                        godebug.emit('notFound', data);
                        break;
                    case 500:
                        godebug.emit('httpError', data);
                        break;
                    default:
                        break;
                }
            }
        });
        try {
            send.apply(this, arguments);
        } catch (error) {
            _log('****************');
            _log(error);
        }
        // _log(this.responseURL)
        godebug.emit('send', {
            reqUrl: this.reqUrl
        });
    };
})();

var global$1 = window;

// 重写监s听
var rewriteEventListener = function rewriteEventListener() {
    if (!window.Element) {
        Element = function Element() {};
        var __createElement = document.createElement;
        document.createElement = function (tagName) {
            var element = __createElement(tagName);
            if (element == null) {
                return null;
            }
            for (var key in Element.prototype) {
                element[key] = Element.prototype[key];
            }return element;
        };

        var __getElementById = document.getElementById;
        document.getElementById = function (id) {
            var element = __getElementById(id);
            if (element == null) {
                return null;
            }
            for (var key in Element.prototype) {
                element[key] = Element.prototype[key];
            }return element;
        };
    }

    var _HTMLElement = window.HTMLElement || Element;
    var _Element = _HTMLElement.prototype;
    var createElement = document.createElement;
    document.onclick = function (e) {
        if (e.target.onclick) {
            godebug.emit('onclick', e);
        }
    };

    var addEventListener = _Element.addEventListener;
    var removeEventListener = _Element.removeEventListener;
    var cache = {};
    if (addEventListener) {
        // 重写监听事件
        _Element.addEventListener = function () {
            addEventListener.apply(this, arguments);
            if (godebug.reportEvent.indexOf(arguments[0]) == -1) {
                return false;
            }
            var name = 'fun' + +new Date();
            cache[name] = function (params) {
                var type = arguments[0].type;
                godebug.emit(type);
            };
            arguments[1] = cache[name];
            this.setAttribute('godebug', name);
            addEventListener.apply(this, arguments);
        };
        _Element.removeEventListener = function (params) {
            removeEventListener.apply(this, arguments);
            if (godebug.reportEvent.indexOf(arguments[0]) == -1) {
                return false;
            }
            var name = this.getAttribute('godebug');
            arguments[1] = cache[name];
            removeEventListener.apply(this, arguments);
        };
    }
};
// dom解析完毕
var completed = function completed() {
    // console.log('read')
    godebug.isReady = true;
    godebug.emit('ready');
};

var registerCompleted = function registerCompleted() {
    if (document.readyState === "complete") {
        completed();
    } else if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", completed, false);
    } else {
        document.attachEvent("onreadystatechange", completed);

        // A fallback to window.onload, that will always work
        window.attachEvent("onload", completed);

        // If IE and not a frame
        // continually check to see if the document is ready
        var top = false;

        try {
            top = window.frameElement == null && document.documentElement;
        } catch (e) {}

        if (top && top.doScroll) {
            (function doScrollCheck() {
                if (!godebug.isReady) {

                    try {
                        // Use the trick by Diego Perini
                        // http://javascript.nwbox.com/IEContentLoaded/
                        top.doScroll("left");
                    } catch (e) {
                        return setTimeout(doScrollCheck, 50);
                    }

                    // detach all dom ready events
                    detach();

                    // and execute any waiting functions
                    completed();
                }
            })();
        }
    }
};

function registerError() {
    // type 错误事件类型
    // message 错误信息
    // lineno  行号
    // colno 列号
    // url 文件名
    // stack 堆栈信息
    // timeStamp 错误时间
    // if (global.addEventListener) {
    global$1.addEventListener('error', function (e) {
        // 主要监控资源加载错误
        // if(e.st)
        if (!e.target.src) {
            return false;
        }
        // _log('a-b=',e.target.src)
        var data = {
            type: e.type,
            message: e.message,
            lineno: e.lineno,
            colno: e.colno,
            url: e.filename,
            // stack:e.error.stack,
            timeStamp: e.timeStamp
        };
        godebug.emit.call(godebug, 'resourceError', data);
    }, true);
    //  } else
    {

        //储存绑定过onerror,要回掉回去
        var _onerror = global$1.onerror;
        global$1.onerror = function (msg, fileName, lineno, colno, error) {
            //_log(error)
            // console.log("msg:" + msg)
            // console.log("url:" + url)
            // console.log("line:" + lineno)
            // console.log("col:" + colno)
            // console.log("error:" + error)
            // console.log(JSON.stringify(global.event))
            // console.log('***************************************')
            //  _log(colno)
            if (!colno) {
                colno = global$1.event.errorCharacter;
            }
            var data = {
                type: 'error',
                name: error && error.name,
                message: msg,
                lineno: lineno,
                colno: colno,
                fileName: fileName,
                stack: error && error.stack
                // timeStamp: 'e.timeStamp',
            };
            // console.log(JSON.stringify(data))
            godebug.emit.call(godebug, 'error', data);
            _onerror && _onerror.apply(this, arguments);
        };
    }
}

function registerRequest() {
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", function () {
            // M = {
            //     url: global.location.href,
            //     title: document.title
            // }
            //  _log(location.href)
        });
    } else {
        document.attachEvent("onreadystatechange", function () {

            _log('_this.readyState');
            // M = {
            //     url: global.location.href,
            //     title: document.title
            // }
            //   _log( global.location.href)
        });
    }
}

function registerEvent() {
    registerCompleted();
    registerError();
    registerRequest();
}
(function () {
    rewriteEventListener();
    registerEvent();
})();

return godebug;

})));
